From 912ad3b6989c9206fea95afe9a0fd03f8413db08 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 3 Mar 2012 23:42:54 -0500 Subject: [PATCH] Break out press-and-hold code as its own object https://bugzilla.gnome.org/show_bug.cgi?id=671057 --- gtk/Makefile.am | 2 + gtk/gtkcolorplane.c | 58 +++++++++ gtk/gtkcolorswatch.c | 95 +++++---------- gtk/gtkpressandhold.c | 226 +++++++++++++++++++++++++++++++++++ gtk/gtkpressandholdprivate.h | 68 +++++++++++ 5 files changed, 383 insertions(+), 66 deletions(-) create mode 100644 gtk/gtkpressandhold.c create mode 100644 gtk/gtkpressandholdprivate.h diff --git a/gtk/Makefile.am b/gtk/Makefile.am index 9f4f8182e2..bb60b3ddb0 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -470,6 +470,7 @@ gtk_private_h_sources = \ gtkorientableprivate.h \ gtkpango.h \ gtkpathbar.h \ + gtkpressandholdprivate.h \ gtkprintoperation-private.h \ gtkprintutils.h \ gtkprivate.h \ @@ -707,6 +708,7 @@ gtk_base_c_sources = \ gtkpango.c \ gtkpapersize.c \ gtkpathbar.c \ + gtkpressandhold.c \ gtkprintcontext.c \ gtkprintoperation.c \ gtkprintoperationpreview.c \ diff --git a/gtk/gtkcolorplane.c b/gtk/gtkcolorplane.c index c8b2d71145..c1ca89891f 100644 --- a/gtk/gtkcolorplane.c +++ b/gtk/gtkcolorplane.c @@ -22,6 +22,7 @@ #include "gtkaccessible.h" #include "gtkadjustment.h" #include "gtkcolorutils.h" +#include "gtkpressandholdprivate.h" #include "gtkintl.h" struct _GtkColorPlanePrivate @@ -32,6 +33,8 @@ struct _GtkColorPlanePrivate cairo_surface_t *surface; gboolean in_drag; + + GtkPressAndHold *press_and_hold; }; G_DEFINE_TYPE (GtkColorPlane, gtk_color_plane, GTK_TYPE_DRAWING_AREA) @@ -285,6 +288,58 @@ plane_motion_notify (GtkWidget *widget, return TRUE; } +static void +hold_action (GtkPressAndHold *pah, + gint x, + gint y, + GtkColorPlane *plane) +{ + gboolean handled; + + g_signal_emit_by_name (plane, "popup-menu", &handled); +} + +static void +tap_action (GtkPressAndHold *pah, + gint x, + gint y, + GtkColorPlane *plane) +{ + update_color (plane, x, y); +} + +static gboolean +plane_touch (GtkWidget *widget, + GdkEventTouch *event) +{ + GtkColorPlane *plane = GTK_COLOR_PLANE (widget); + + if (!plane->priv->press_and_hold) + { + gint drag_threshold; + + g_object_get (gtk_widget_get_settings (widget), + "gtk-dnd-drag-threshold", &drag_threshold, + NULL); + + plane->priv->press_and_hold = gtk_press_and_hold_new (); + + g_object_set (plane->priv->press_and_hold, + "drag-threshold", drag_threshold, + "hold-time", 1000, + NULL); + + g_signal_connect (plane->priv->press_and_hold, "hold", + G_CALLBACK (hold_action), plane); + g_signal_connect (plane->priv->press_and_hold, "tap", + G_CALLBACK (tap_action), plane); + } + + gtk_press_and_hold_process_event (plane->priv->press_and_hold, (GdkEvent *)event); + + return TRUE; +} + static void sv_move (GtkColorPlane *plane, gdouble ds, @@ -406,6 +461,8 @@ plane_finalize (GObject *object) g_clear_object (&plane->priv->s_adj); g_clear_object (&plane->priv->v_adj); + g_clear_object (&plane->priv->press_and_hold); + G_OBJECT_CLASS (gtk_color_plane_parent_class)->finalize (object); } @@ -424,6 +481,7 @@ gtk_color_plane_class_init (GtkColorPlaneClass *class) widget_class->motion_notify_event = plane_motion_notify; widget_class->grab_broken_event = plane_grab_broken; widget_class->key_press_event = plane_key_press; + widget_class->touch_event= plane_touch; g_type_class_add_private (class, sizeof (GtkColorPlanePrivate)); } diff --git a/gtk/gtkcolorswatch.c b/gtk/gtkcolorswatch.c index c75ef2d3d1..810112db1a 100644 --- a/gtk/gtkcolorswatch.c +++ b/gtk/gtkcolorswatch.c @@ -28,20 +28,12 @@ #include "gtkmenu.h" #include "gtkmenuitem.h" #include "gtkmenushell.h" +#include "gtkpressandholdprivate.h" #include "gtkprivate.h" #include "gtkintl.h" #include "a11y/gtkcolorswatchaccessible.h" -typedef struct { - GtkWidget *widget; - - GdkEventSequence *sequence; - guint press_and_hold_id; - gint start_x; - gint start_y; -} GtkPressAndHoldData; - struct _GtkColorSwatchPrivate { GdkRGBA color; @@ -54,7 +46,7 @@ struct _GtkColorSwatchPrivate GdkWindow *event_window; - GtkPressAndHoldData *press_and_hold; + GtkPressAndHold *press_and_hold; }; enum @@ -531,80 +523,52 @@ swatch_button_release (GtkWidget *widget, } static void -swatch_press_and_hold_cancel (GtkWidget *widget, - GtkPressAndHoldData *data) +hold_action (GtkPressAndHold *pah, + gint x, + gint y, + GtkColorSwatch *swatch) { - if (data->press_and_hold_id) - { - g_source_remove (data->press_and_hold_id); - data->press_and_hold_id = 0; - } - - data->sequence = NULL; + emit_customize (swatch); } static void -swatch_press_and_hold_free (GtkPressAndHoldData *data) +tap_action (GtkPressAndHold *pah, + gint x, + gint y, + GtkColorSwatch *swatch) { - swatch_press_and_hold_cancel (data->widget, data); - g_slice_free (GtkPressAndHoldData, data); -} - -static gboolean -swatch_press_and_hold_action (gpointer data) -{ - GtkPressAndHoldData *pah = data; - - emit_customize (GTK_COLOR_SWATCH (pah->widget)); - swatch_press_and_hold_cancel (pah->widget, pah); - - return G_SOURCE_REMOVE; + swatch_primary_action (swatch); } - static gboolean swatch_touch (GtkWidget *widget, GdkEventTouch *event) { GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget); - GtkPressAndHoldData *data; if (!swatch->priv->press_and_hold) - swatch->priv->press_and_hold = g_slice_new0 (GtkPressAndHoldData); + { + gint drag_threshold; - data = swatch->priv->press_and_hold; + g_object_get (gtk_widget_get_settings (widget), + "gtk-dnd-drag-threshold", &drag_threshold, + NULL); - /* We're already tracking a different touch, ignore */ - if (data->sequence != NULL && data->sequence != event->sequence) - return TRUE; + swatch->priv->press_and_hold = gtk_press_and_hold_new (); - if (event->type == GDK_TOUCH_BEGIN) - { - data->widget = widget; - data->sequence = event->sequence; - data->start_x = event->x; - data->start_y = event->y; + g_object_set (swatch->priv->press_and_hold, + "drag-threshold", drag_threshold, + "hold-time", 1000, + NULL); - data->press_and_hold_id = - gdk_threads_add_timeout (1000, swatch_press_and_hold_action, data); - } - else if (event->type == GDK_TOUCH_UPDATE) - { - if (gtk_drag_check_threshold (widget, - data->start_x, data->start_y, - event->x, event->y)) - swatch_press_and_hold_cancel (widget, data); - } - else if (event->type == GDK_TOUCH_END) - { - swatch_press_and_hold_cancel (widget, data); - swatch_primary_action (swatch); - } - else if (event->type == GDK_TOUCH_CANCEL) - { - swatch_press_and_hold_cancel (widget, data); + g_signal_connect (swatch->priv->press_and_hold, "hold", + G_CALLBACK (hold_action), swatch); + g_signal_connect (swatch->priv->press_and_hold, "tap", + G_CALLBACK (tap_action), swatch); } + gtk_press_and_hold_process_event (swatch->priv->press_and_hold, (GdkEvent *)event); + return TRUE; } @@ -759,8 +723,7 @@ swatch_finalize (GObject *object) GtkColorSwatch *swatch = GTK_COLOR_SWATCH (object); g_free (swatch->priv->icon); - if (swatch->priv->press_and_hold) - swatch_press_and_hold_free (swatch->priv->press_and_hold); + g_clear_object (&swatch->priv->press_and_hold); G_OBJECT_CLASS (gtk_color_swatch_parent_class)->finalize (object); } diff --git a/gtk/gtkpressandhold.c b/gtk/gtkpressandhold.c new file mode 100644 index 0000000000..aed0095d60 --- /dev/null +++ b/gtk/gtkpressandhold.c @@ -0,0 +1,226 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 2012 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#include "config.h" + +#include "gdk.h" +#include "gtkpressandholdprivate.h" +#include "gtkintl.h" +#include "gtkprivate.h" + +struct _GtkPressAndHoldPrivate +{ + gint hold_time; + gint drag_threshold; + + GdkEventSequence *sequence; + guint timeout; + gint start_x; + gint start_y; + gint x; + gint y; +}; + +enum +{ + PROP_ZERO, + PROP_HOLD_TIME, + PROP_DRAG_THRESHOLD +}; + +enum +{ + HOLD, + TAP, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL]; + +G_DEFINE_TYPE (GtkPressAndHold, gtk_press_and_hold, G_TYPE_OBJECT) + +static void +gtk_press_and_hold_init (GtkPressAndHold *pah) +{ + pah->priv = G_TYPE_INSTANCE_GET_PRIVATE (pah, + GTK_TYPE_PRESS_AND_HOLD, + GtkPressAndHoldPrivate); + + pah->priv->hold_time = 1000; + pah->priv->drag_threshold = 8; +} + +static void +press_and_hold_finalize (GObject *object) +{ + GtkPressAndHold *pah = GTK_PRESS_AND_HOLD (object); + + if (pah->priv->timeout) + g_source_remove (pah->priv->timeout); + + G_OBJECT_CLASS (gtk_press_and_hold_parent_class)->finalize (object); +} + +static void +press_and_hold_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkPressAndHold *pah = GTK_PRESS_AND_HOLD (object); + + switch (prop_id) + { + case PROP_HOLD_TIME: + g_value_set_int (value, pah->priv->hold_time); + break; + case PROP_DRAG_THRESHOLD: + g_value_set_int (value, pah->priv->drag_threshold); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +press_and_hold_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkPressAndHold *pah = GTK_PRESS_AND_HOLD (object); + + switch (prop_id) + { + case PROP_HOLD_TIME: + pah->priv->hold_time = g_value_get_int (value); + break; + case PROP_DRAG_THRESHOLD: + pah->priv->hold_time = g_value_get_int (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_press_and_hold_class_init (GtkPressAndHoldClass *class) +{ + GObjectClass *object_class = (GObjectClass *)class; + + object_class->get_property = press_and_hold_get_property; + object_class->set_property = press_and_hold_set_property; + object_class->finalize = press_and_hold_finalize; + + signals[HOLD] = + g_signal_new ("hold", + GTK_TYPE_PRESS_AND_HOLD, + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GtkPressAndHoldClass, hold), + NULL, NULL, NULL, + G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT); + + signals[TAP] = + g_signal_new ("tap", + GTK_TYPE_PRESS_AND_HOLD, + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GtkPressAndHoldClass, tap), + NULL, NULL, NULL, + G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT); + + g_object_class_install_property (object_class, PROP_HOLD_TIME, + g_param_spec_int ("hold-time", P_("Hold Time"), P_("Hold Time (in milliseconds)"), + 0, G_MAXINT, 1000, GTK_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_DRAG_THRESHOLD, + g_param_spec_int ("drag-threshold", P_("Drag Threshold"), P_("Drag Threshold (in pixels)"), + 1, G_MAXINT, 8, GTK_PARAM_READWRITE)); + + g_type_class_add_private (object_class, sizeof (GtkPressAndHoldPrivate)); +} + +static void +press_and_hold_cancel (GtkPressAndHold *pah) +{ + GtkPressAndHoldPrivate *priv = pah->priv; + + if (priv->timeout) + g_source_remove (priv->timeout); + + priv->timeout = 0; + priv->sequence = NULL; +} + +static gboolean +hold_action (gpointer data) +{ + GtkPressAndHold *pah = data; + GtkPressAndHoldPrivate *priv = pah->priv; + + press_and_hold_cancel (pah); + + g_signal_emit (pah, signals[HOLD], 0, priv->x, priv->y); + + return G_SOURCE_REMOVE; +} + +void +gtk_press_and_hold_process_event (GtkPressAndHold *pah, + GdkEvent *event) +{ + GtkPressAndHoldPrivate *priv = pah->priv; + + /* We're already tracking a different touch, ignore */ + if (priv->sequence != NULL && priv->sequence != event->touch.sequence) + return; + + priv->x = event->touch.x; + priv->y = event->touch.y; + + if (event->type == GDK_TOUCH_BEGIN) + { + priv->sequence = event->touch.sequence; + priv->start_x = priv->x; + priv->start_y = priv->y; + + priv->timeout = + gdk_threads_add_timeout (priv->hold_time, hold_action, pah); + } + else if (event->type == GDK_TOUCH_UPDATE) + { + if (ABS (priv->x - priv->start_x) > priv->drag_threshold || + ABS (priv->y - priv->start_y) > priv->drag_threshold) + press_and_hold_cancel (pah); + } + else if (event->type == GDK_TOUCH_END) + { + press_and_hold_cancel (pah); + g_signal_emit (pah, signals[TAP], 0, priv->x, priv->y); + } + else if (event->type == GDK_TOUCH_CANCEL) + { + press_and_hold_cancel (pah); + } +} + +GtkPressAndHold * +gtk_press_and_hold_new (void) +{ + return (GtkPressAndHold *) g_object_new (GTK_TYPE_PRESS_AND_HOLD, NULL); +} diff --git a/gtk/gtkpressandholdprivate.h b/gtk/gtkpressandholdprivate.h new file mode 100644 index 0000000000..ccccca24f9 --- /dev/null +++ b/gtk/gtkpressandholdprivate.h @@ -0,0 +1,68 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 2012 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __GTK_PRESS_AND_HOLD_H__ +#define __GTK_PRESS_AND_HOLD_H__ + +G_BEGIN_DECLS + +#define GTK_TYPE_PRESS_AND_HOLD (gtk_press_and_hold_get_type ()) +#define GTK_PRESS_AND_HOLD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_PRESS_AND_HOLD, GtkPressAndHold)) +#define GTK_PRESS_AND_HOLD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PRESS_AND_HOLD, GtkPressAndHoldClass)) +#define GTK_IS_PRESS_AND_HOLD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_PRESS_AND_HOLD)) +#define GTK_IS_PRESS_AND_HOLD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PRESS_AND_HOLD)) +#define GTK_PRESS_AND_HOLD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PRESS_AND_HOLD, GtkPressAndHoldClass)) + + +typedef struct _GtkPressAndHold GtkPressAndHold; +typedef struct _GtkPressAndHoldClass GtkPressAndHoldClass; +typedef struct _GtkPressAndHoldPrivate GtkPressAndHoldPrivate; + +struct _GtkPressAndHold +{ + GObject parent; + + /*< private >*/ + GtkPressAndHoldPrivate *priv; +}; + +struct _GtkPressAndHoldClass +{ + GObjectClass parent_class; + + void ( * hold) (GtkPressAndHold *pah, gint x, gint y); + void ( * tap) (GtkPressAndHold *pah, gint x, gint y); +}; + + +G_GNUC_INTERNAL +GType gtk_press_and_hold_get_type (void) G_GNUC_CONST; + +G_GNUC_INTERNAL +GtkPressAndHold * gtk_press_and_hold_new (void); + +G_GNUC_INTERNAL +void gtk_press_and_hold_process_event (GtkPressAndHold *pah, + GdkEvent *event); + +G_END_DECLS + +#endif /* __GTK_PRESS_AND_HOLD_H__ */ -- 2.30.2